CWOI20231024A 赢钱

简要题意:

初始有 元钱,然后每次可以花费 )元,有 的概率获得 元, 的概率不获得钱。问获得 )元的概率。对 取模。

首先若 ,答案是 ,因为假如你每次投入 元,当 时,若投入 元,应增加 元,而减少 元,总共增加 元,那么钱数总是在增多的,那么必定可以达到

现在来解决 。有一个神奇的地方在于,若规定只玩 轮,可以将 划分成 段,每段内部概率相同。就拿样例来说:。若只玩 轮,那么, 成功的概率是 成功的概率是 。若只玩 轮,那么, 成功的概率是 成功的概率是 成功的概率是 成功的概率是

我们可以把 用一个二进制小数表示,来区分每个段。只玩前 位小数,就是只玩 轮。

考虑从后往前 dp。 我们记 表示只玩小数点后第 位及以后的,希望能玩到“向第 位进位” ,成功的概率是多少。显然, 就是答案。转移的话,分第 位为 讨论。 如果第 位为 ,那么 ,也就是必须下一位能进位,并且再玩一次才能进位。否则,,表示如果下一位成功进位了,由于这一位是 ,能直接再往前进,否则的话需要玩一次,能获得 元则能进位。

但是这只能解决 的情况,也就是 在某个整段的端点上。否则 是一个无限循环小数。

但是循环小数好啊。考虑求循环节的过程(竖式模拟除法),发现,若当前被除数是 ,且之前存在过一个 那么就有一个循环节。由于除数 不变,所以 可以理解为解决了同一个问题!

那么,设这个循环节起始位置是 ,终止位置是 )那么有

这就是一个线性方程。 可以线性地推到 。具体地,设 ,有 。若 ,那么 都是一个系数)。自然可以推出 。求解 即可得到 。然后就是一个有限的 dp 问题了,简单求解即可。

代码:

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int P = 998244353, Q = 616898040;

int a, z, q, i, j;
int vis[2000008];
bitset<2000008> tp;

int Qpow(int x, int y) 
{
    int ret = 1; 
    for (; y; y >>= 1) {
        if (y & 1) ret = ret * x % P;
        x = x * x % P;
    }
    return ret;
}

signed main() 
{
    cin >> a >> z >> q;
    for (i = 1; !vis[a]; i++) {
        vis[a] = i;
        a = a + a;
        if (a >= z) a -= z, tp.set(i);
    }
    int k = 1, b = 0, p = q * Q % P; q = 1 + P - p;
    for (j = vis[a], i--; i >= j; i--) {
        if (tp[i]) k = k * q % P, b = (b * q + p) % P;
        else k = k * p % P, b = b * p % P;
    }
    k = (P - b) * Qpow(k - 1, P - 2) % P;
    while (--j) {
        if (tp[j]) k = (p + q * k) % P;
        else k = p * k % P;
    }
    cout << k << '\n';
    return 0;
}